#include "ak_internal.h"
#include "ak_probe.h"
#include "lsm_public.h"
/* For exporting variables and functions. */
struct ak_security_exports ak_sec_exports;
/* Members are updated by loadable kernel module. */
struct ak_security_operations ak_security_ops;

int check_procname(char *procname)
{
    if (0 == strcmp(procname, "/usr/bin/vi") ||
        0 == strcmp(procname, "/usr/bin/vim") ||
        0 == strcmp(procname, "/usr/bin/touch") ||
        0 == strcmp(procname, "/usr/bin/cat") ||
        0 == strcmp(procname, "/usr/bin/chmod") ||
        0 == strcmp(procname, "/usr/bin/rm") ||
        0 == strcmp(procname, "/usr/bin/ls"))
     {
        return 1;
     }
     else
        return 0;
}
int check_filename(char *filename)
{
    if (strstr(filename, "/share/swf/"))
    {
        return 1;
    }
    else
        return 0;
}

int ak360_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode)
{
    char *procname = NULL;
    //struct vfsmount *vfsmnt = sec_vfsmount(dentry); 
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}
	else
    {
        printk("%s get filename failed.\n", __FUNCTION__);
    }
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}

int ak360_inode_mkdir(struct inode *dir, struct dentry *dentry,  umode_t mode)
{
    char *procname = NULL;
        
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
    if (filename)
    {
        if (check_filename(filename))
            printk("%s filename = %s\n", __FUNCTION__, filename);
        kfree(filename);
    }

    procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}

int ak360_inode_rmdir(struct inode *dir,  struct dentry *dentry)
{
    char *procname = NULL;
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
int ak360_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
    char *procname = NULL;
    char *oldfilename = NULL;
    char *newfilename = NULL;
    
    struct path oldpath = {
        .dentry = old_dentry,
        .mnt = NULL,
    };

    struct path newpath = {
        .dentry = new_dentry,
        .mnt = NULL,
    };
    
    oldfilename = ak_get_realpath(&oldpath);
	if (oldfilename)
	{
	    if (check_filename(oldfilename))
		    printk("%s oldfilename = %s\n", __FUNCTION__, oldfilename);
		kfree(oldfilename);
	}

	newfilename = ak_get_realpath(&newpath);
	if (newfilename)
	{
	    if (check_filename(newfilename))
		    printk("%s newfilename = %s\n", __FUNCTION__, newfilename);
		kfree(newfilename);
	}
	
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
int ak360_inode_unlink(struct inode *dir, struct dentry *dentry)
{
    char *procname = NULL;
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename) {
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
int ak360_inode_rename(struct inode *old_inode,
                      struct dentry *old_dentry,
                      struct inode *new_inode,
                      struct dentry *new_dentry)
{
    char *oldfilename = NULL;
    char *newfilename = NULL;
    char *procname = NULL;
    
    struct path oldpath = {
        .dentry = old_dentry,
        .mnt = NULL,
    };

    struct path newpath = {
        .dentry = new_dentry,
        .mnt = NULL,
    };
    
    oldfilename = ak_get_realpath(&oldpath);
	if (oldfilename)
	{
	    if (check_filename(oldfilename))
		    printk("%s oldfilename = %s\n", __FUNCTION__, oldfilename);
		kfree(oldfilename);
	}

	newfilename = ak_get_realpath(&newpath);
	if (newfilename)
	{
	    if (check_filename(newfilename))
		    printk("%s newfilename = %s\n", __FUNCTION__, newfilename);
		kfree(newfilename);
	}
	
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}
int ak360_inode_symlink(struct inode *inode, struct dentry *dentry, const char *old_name)
{
    struct path path = {
        .dentry = dentry,
        .mnt = NULL,
    };
    
    char *filename = ak_get_realpath(&path);
	if (filename)
	{
	    if (check_filename(filename))
		    printk("%s filename = %s\n", __FUNCTION__, filename);
		kfree(filename);
	}

    return 0;
}

static int ak360_bprm_check_security(struct linux_binprm *bprm)
{
	char *procname = ak_get_file_realpath(bprm->file);
    if (procname)
    {
        //if (check_procname(procname))
        //printk("%s procname is %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    if (NULL != bprm)
    {
        //printk("current->fs->pwd = %s\n",current->fs->pwd.mnt->mnt_root->d_name.name);
        //printk("current->fs->root = %s\n",current->fs->root.mnt->mnt_root->d_name.name);
        //printk("bprm->filename is %s\n",bprm->filename);
    }
    return 0;
}

static int ak360_file_permission(struct file *file, int mask)
{
    char *procname = NULL;
	char *filename = ak_get_file_realpath(file);
    if (filename)
    {
        if (check_filename(filename))
            printk("%s filename is %s, mask is %d\n", __FUNCTION__, filename,mask);
        kfree(filename);
    }
    else
    {
        printk("%s get filename failed.\n", __FUNCTION__);
    }
    procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
            printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

    return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || (defined(RHEL_MAJOR) && RHEL_MAJOR == 8)
/**
 * ak360_file_open - Check permission for open().
 *
 * @f:    Pointer to "struct file".
 *
 * Returns 0 on success, negative value otherwise.
 */
static int ak360_file_open(struct file *file)
{
	char *procname = NULL;
    char *filename = ak_get_file_realpath(file);
    if (filename)
    {
        if (check_filename(filename))
            printk("%s filename is %s\n", __FUNCTION__, filename);
        kfree(filename);
    }
    else
    {
        printk("%s get filename failed.\n", __FUNCTION__);
    }
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

	return 0;
}
#else
/**
 * ak360_file_open - Check permission for open().
 *
 * @f:    Pointer to "struct file".
 * @cred: Pointer to "struct cred".
 *
 * Returns 0 on success, negative value otherwise.
 */
static int ak360_file_open(struct file *file, const struct cred *cred)
{
	char *procname = NULL;
    char *filename = ak_get_file_realpath(file);
    if (filename)
    {
        if (check_filename(filename))
            printk("%s filename is %s\n", __FUNCTION__, filename);
        kfree(filename);
    }
    else
    {
        printk("%s get filename failed.\n", __FUNCTION__);
    }
	procname = (char *)ak_get_procname();
    if (procname)
    {
        if (check_procname(procname))
        printk("%s procname = %s\n", __FUNCTION__, procname);
        kfree(procname);
    }

	return 0;
}
#endif

#define MY_HOOK_INIT(HEAD, HOOK)				\
	{ .head = &probe_dummy_security_hook_heads.HEAD,	\
			.hook = { .HEAD = HOOK } }

static struct security_hook_list ak360_hooks[] = {
    MY_HOOK_INIT(inode_create, ak360_inode_create),
    MY_HOOK_INIT(inode_mkdir, ak360_inode_mkdir),
    MY_HOOK_INIT(inode_rmdir, ak360_inode_rmdir),
    MY_HOOK_INIT(inode_link, ak360_inode_link),
    MY_HOOK_INIT(inode_unlink, ak360_inode_unlink),
    MY_HOOK_INIT(inode_symlink, ak360_inode_symlink),
    MY_HOOK_INIT(inode_rename, ak360_inode_rename),
    
    MY_HOOK_INIT(bprm_check_security, ak360_bprm_check_security),
    MY_HOOK_INIT(file_permission, ak360_file_permission),
    MY_HOOK_INIT(file_open, ak360_file_open),

};
static inline void add_hook(struct security_hook_list *hook)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
    hlist_add_tail_rcu(&hook->list, hook->head);
#else
    list_add_tail_rcu(&hook->list, hook->head);
#endif
}

#if defined(CONFIG_STRICT_KERNEL_RWX) && !defined(CONFIG_SECURITY_WRITABLE_HOOKS)
#include <linux/uaccess.h> /* probe_kernel_write() */
#define NEED_TO_CHECK_HOOKS_ARE_WRITABLE
    
#if defined(CONFIG_X86)
#define MAX_RO_PAGES 1024
static struct page *ro_pages[MAX_RO_PAGES] __initdata;
static unsigned int ro_pages_len __initdata;

static bool __init lsm_test_page_ro(void *addr)
{
    unsigned int i;
    int unused;
    struct page *page;

    page = (struct page *) lookup_address((unsigned long) addr, &unused);
    if (!page)
        return false;
    if (test_bit(_PAGE_BIT_RW, &(page->flags)))
        return true;
    for (i = 0; i < ro_pages_len; i++)
        if (page == ro_pages[i])
            return true;
    if (ro_pages_len == MAX_RO_PAGES)
        return false;
    ro_pages[ro_pages_len++] = page;
    return true;
}

static bool __init check_ro_pages(struct security_hook_heads *hooks)
{
    int i;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
    struct hlist_head *list = &hooks->capable;

    if (!probe_kernel_write(list, list, sizeof(void *)))
        return true;
    for (i = 0; i < ARRAY_SIZE(ak360_hooks); i++) {
        struct hlist_head *head = ak360_hooks[i].head;
        struct security_hook_list *shp;

        if (!lsm_test_page_ro(&head->first))
            return false;
        hlist_for_each_entry(shp, head, list)
            if (!lsm_test_page_ro(&shp->list.next) ||
                !lsm_test_page_ro(&shp->list.pprev))
                return false;
    }
#else
    struct list_head *list = &hooks->capable;

    if (!probe_kernel_write(list, list, sizeof(void *)))
        return true;
    for (i = 0; i < ARRAY_SIZE(ak360_hooks); i++) {
        struct list_head *head = ak360_hooks[i].head;
        struct security_hook_list *shp;

        if (!lsm_test_page_ro(&head->next) ||
            !lsm_test_page_ro(&head->prev))
            return false;
        list_for_each_entry(shp, head, list)
            if (!lsm_test_page_ro(&shp->list.next) ||
                !lsm_test_page_ro(&shp->list.prev))
                return false;
    }
#endif
    return true;
}
#else
static bool __init check_ro_pages(struct security_hook_heads *hooks)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
    struct hlist_head *list = &hooks->capable;
#else
    struct list_head *list = &hooks->capable;
#endif

    return !probe_kernel_write(list, list, sizeof(void *));
}
#endif
#endif


/**
* ccs_init - Initialize this module.
*
* Returns 0 on success, negative value otherwise.
*/

int __init ak_sec_init(void)
{
    int idx;
    struct security_hook_heads *hooks = probe_security_hook_heads();
    if (!hooks)
        goto out;
    for (idx = 0; idx < ARRAY_SIZE(ak360_hooks); idx++)
        ak360_hooks[idx].head = ((void *) hooks)
            + ((unsigned long) ak360_hooks[idx].head)
            - ((unsigned long) &probe_dummy_security_hook_heads);
#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE)
    if (!check_ro_pages(hooks)) {
        printk(KERN_INFO "Can't update security_hook_heads due to write protected. Retry with rodata=0 kernel command line option added.\n");
        return -EINVAL;
    }
#endif
    ak_sec_exports.find_task_by_vpid = probe_find_task_by_vpid();
    if (!ak_sec_exports.find_task_by_vpid)
        goto out;
    ak_sec_exports.find_task_by_pid_ns = probe_find_task_by_pid_ns();
    if (!ak_sec_exports.find_task_by_pid_ns)
        goto out;
    ak_sec_exports.d_absolute_path = probe_d_absolute_path();
    if (!ak_sec_exports.d_absolute_path)
        goto out;
#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86)
    for (idx = 0; idx < ro_pages_len; idx++)
        set_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags));
#endif
    for (idx = 0; idx < ARRAY_SIZE(ak360_hooks); idx++)
        add_hook(&ak360_hooks[idx]);
#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86)
    for (idx = 0; idx < ro_pages_len; idx++)
        clear_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags));
#endif
    printk(KERN_INFO "AKARI: 1.0.40   2019/12/25\n");
    printk(KERN_INFO
           "Access Keeping And Regulating Instrument registered.\n");
    return 0;
out:
    return -EINVAL;

}


void ak_sec_exit(void)
{
    unsigned long cr0 = clear_and_return_cr0();
    security_delete_hooks(ak360_hooks, ARRAY_SIZE(ak360_hooks));
    setback_cr0(cr0);
    return;
}

